import { Entity } from "../../../../../Core/Module/Entity/Entity";
import { AwakeSystem, IAwake } from "../../../../../Core/Module/EventSystem/IAwakeSystem";
import { DestroySystem, IDestroy } from "../../../../../Core/Module/EventSystem/IDestroySystem";
import { TimerComponent } from "../../../../../Core/Module/Timer/TimerComponent";
import { ExpandOf, FriendOf, GetTypeOf, ImplementsInterface, ObjectSystemAttribute, Type, TypeOf } from "../../../../../Decorator/Decorator";
import { ETCancellationToken } from "../../../../../Support/Share/Lib/Async/ETCancellationToken";
import { ETTask } from "../../../../../Support/Share/Lib/Async/ETTask";
import { Dictionary } from "../../../../../Support/Share/Lib/Dictionary";

export class WaitTypeError {
    public static readonly Success = 0;
    public static readonly Destroy = 1;
    public static readonly Cancel = 2;
    public static readonly Timeout = 3;
}

export interface IWaitType {

    Error: number;

}

export class IWaitType {

}

interface IDestroyRun {

    SetResult(): void;

}

class ResultCallback<K extends IWaitType> implements IDestroyRun {

    private tcs: ETTask<K>;

    constructor() {
        this.tcs = ETTask.Create<K>();
    }

    public get Task(): ETTask<K> {
        return this.tcs;
    }

    public get IsDisposed(): boolean {
        return this.tcs == null;
    }

    SetResult(k?: K): void {
        var t = this.tcs;
        this.tcs = null;
        t.SetResult(k);
    }

}

@ImplementsInterface(IAwake)
@ImplementsInterface(IDestroy)
export class ObjectWait extends Entity implements IAwake, IDestroy {

    public tcss: Dictionary<Type, Object> = new Dictionary<Type, Object>();

}

/**
 * 使用 Expand 扩展方法
 * 
 * 必须作出以下申明，才能调用对象的扩展方法
 */
declare module "./ObjectWait" {

    interface ObjectWait {
        Wait<T extends IWaitType>(waitType: typeof IWaitType, timeout?: number, cancellationToken?: ETCancellationToken): Promise<T>;
        Notify<T extends IWaitType>(obj: T): void;
    }

}

@ExpandOf(ObjectWait)
export class ObjectWaitSystem {

    private static async Wait<T extends IWaitType>(self: ObjectWait, waitType: typeof IWaitType, timeout: number = 0, cancellationToken: ETCancellationToken = null): Promise<T> {
        let type = TypeOf(waitType);
        let tcs = new ResultCallback<T>();
        if (timeout != 0) {
            let WaitTimeout = async () => {
                await TimerComponent.Instance.WaitAsync(timeout);
                if (tcs.IsDisposed) {
                    return;
                }
                let wt = new waitType();
                wt.Error = WaitTypeError.Timeout;
                self.Notify(wt);
            }
            WaitTimeout().then();
        }
        self.tcss.Add(type, tcs);
        let CancelAction = () => {
            let wt = new waitType();
            wt.Error = WaitTypeError.Cancel;
            self.Notify(wt);
        }
        let ret: T = null;
        try {
            cancellationToken?.Add(CancelAction);
            ret = await tcs.Task;
        } finally {
            cancellationToken?.Remove(CancelAction);
        }
        return ret;
    }

    private static Notify<T extends IWaitType>(self: ObjectWait, obj: T): void {
        let type: Type = GetTypeOf(obj);
        let tcs = self.tcss.TryGetValue(type);
        if (!tcs) {
            return;
        }
        self.tcss.Remove(type);
        (<ResultCallback<T>>tcs).SetResult(obj);
    }

}

@FriendOf(ObjectWait)
@ObjectSystemAttribute()
export class ObjectWaitAwakeSystem extends AwakeSystem<ObjectWait> {

    Awake(self: ObjectWait): void {
        self.tcss.Clear();
    }

}

@FriendOf(ObjectWait)
@ObjectSystemAttribute()
export class ObjectWaitDestroySystem extends DestroySystem<ObjectWait> {

    Destroy(self: ObjectWait): void {
        let values = self.tcss.values;
        for (let key in values) {
            (<IDestroyRun>values[key]).SetResult();
        }
    }

}